package com.sunlands.qdcy.tongzhuo.service;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.sunlands.qdcy.tongzhuo.constant.RedisKeyConst;
import com.sunlands.qdcy.tongzhuo.exception.CheckRunException;
import com.sunlands.qdcy.tongzhuo.feign.vo.UserInfoDTO;
import com.sunlands.qdcy.tongzhuo.model.DiaryDO;
import com.sunlands.qdcy.tongzhuo.model.DiaryTopicDO;
import com.sunlands.qdcy.tongzhuo.model.UserDiaryForwardDO;
import com.sunlands.qdcy.tongzhuo.model.UserDiaryReplyDO;
import com.sunlands.qdcy.tongzhuo.repository.UserDiaryForwardRepository;
import com.sunlands.qdcy.tongzhuo.vo.ForwardDiaryVO;
import com.sunlands.qdcy.tongzhuo.vo.ForwardMessageVO;
import com.sunlands.qdcy.tongzhuo.vo.ForwardVO;
import com.sunlands.qdcy.tongzhuo.vo.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.sunlands.qdcy.tongzhuo.model.UserDiaryForwardDO.ForwardType;
import static com.sunlands.qdcy.tongzhuo.model.UserDiaryForwardDO.ForwardType.DIARY;
import static com.sunlands.qdcy.tongzhuo.model.UserDiaryForwardDO.ForwardType.REPLY;
import static com.sunlands.qdcy.tongzhuo.model.UserDiaryForwardDO.StationType;

/**
 * @author mengxiangchao
 */
@Slf4j
@Service
public class ForwardService extends BaseService {

    @Autowired
    UserDiaryForwardRepository forwardRepository;

    @Lazy
    @Autowired
    DiaryService diaryService;

    @Lazy
    @Autowired
    UserDiaryReplyService replyService;

    @Autowired
    DiaryMessageService diaryMessageService;
    @Lazy
    @Autowired
    private UserDiaryTopicService topicService;

    //截取内容大小
    private static final int DISPLAY_LENGTH = 30;

    @Autowired
    private AsyncTaskExecutor defaultExecutor;


    public DiaryDO saveForwardDiary(ForwardDiaryVO forwardDiaryVO, Long userId) {

        if (forwardDiaryVO == null) {
            throw new IllegalArgumentException("转发对象不能为空！");
        }
        if (forwardDiaryVO.getForwardId() == null || forwardDiaryVO.getForwardId() == 0) {
            throw new IllegalArgumentException("被转发日记id 不能为空！");
        }


        DiaryTopicDO topic = null;
        if (forwardDiaryVO.getTopicId() != null) {
            topic = topicService.findDiaryTopic(forwardDiaryVO.getTopicId());
            if (Objects.isNull(topic)) {
                throw new IllegalArgumentException("topic 话题不存在！！topicId = " + forwardDiaryVO.getTopicId());
            }

        }

        Long forwardId = forwardDiaryVO.getForwardId();

        DiaryDO target = diaryService.findById(forwardId);

        if (target == null || target.getCreateType().intValue() == DiaryDO.CreateTypeEnums.DELETE.type) {
            throw new CheckRunException("此日记不存在或已删除");
        }

        //最根本的那个日记的id
        Long rootId = null;
        DiaryDO root = null;

        //是否是转发的自建类型的日记
        if (target.getCreateType().intValue() == DiaryDO.CreateTypeEnums.CREATE_BY_SELF.type) {
            rootId = target.getId();
            root = target;
        } else if (target.getCreateType().intValue() == DiaryDO.CreateTypeEnums.FORWARD.type) {
            rootId = target.getRootId();
            root = diaryService.findById(rootId);
        }
        forwardDiaryVO.setRootId(rootId);

        if (root == null || root.getCreateType().intValue() == DiaryDO.CreateTypeEnums.DELETE.type) {
            throw new CheckRunException("此日记不存在或已删除");
        }

        if (root.getCreateType().intValue() != DiaryDO.CreateTypeEnums.CREATE_BY_SELF.type) {
            log.error("rootId对应的日记，不是自建类型！rootId={}", rootId);
            throw new CheckRunException("该日记不支持转发");
        }

        //保存
        DiaryDO newDiary = from(forwardDiaryVO, userId);
        if (topic != null) {
            newDiary.setTopicName(topic.getTopic());
        }
        newDiary = diaryService.save(newDiary);

        //记录两条转发记录，当然也可能root跟target是一条
        Set<DiaryDO> targetSets = Sets.newHashSet(target, root);

        //以下需要另起线程异步执行
        defaultExecutor.execute(() -> {

            String forwardMessage = subContent(forwardDiaryVO.getContent());

            targetSets.stream().forEach(targetDiary -> {
                //转发记录
                ForwardVO forward = ForwardVO.builder()
                        .forwardMessage(forwardMessage)
                        .createTime(new Date())
                        .fromUserId(userId)
                        .station(StationType.STATION_INNER_MINE.getCode())
                        .targetId(targetDiary.getId())
                        .targetUserId(targetDiary.getUserId())
                        .type(DIARY.getCode())
                        .build();
                log.info("异步保存转发记录：fromUserId={},targetUserId={},targetId={}", userId, targetDiary.getUserId(), targetDiary.getId());
                this.addForward(forward);

            });
        });

        return newDiary;

    }


    private DiaryDO from(ForwardDiaryVO diaryDTO, Long userId) {
        return DiaryDO.builder()
                .content(diaryDTO.getContent())
                .imgUrls(Objects.isNull(diaryDTO.getImgUrls()) || diaryDTO.getImgUrls().isEmpty() ? null : StringUtils.join(diaryDTO.getImgUrls(), ","))
                .topicId(diaryDTO.getTopicId())
                .forwardId(diaryDTO.getForwardId())
                .rootId(diaryDTO.getRootId())
                .createType(DiaryDO.CreateTypeEnums.FORWARD.type)
                .userId(userId)
                .build();
    }


    @Async
    public void addForward(ForwardVO vo) {
        if (vo == null) {
            throw new IllegalArgumentException("参数对象不能为空！");
        }
        if (vo.getType() == null) {
            throw new IllegalArgumentException("转发类型不能为空！");
        }
        if (ForwardType.create(vo.getType()) == null) {
            throw new IllegalArgumentException("非法的类型参数！");
        }
        if (vo.getTargetId() == null || vo.getTargetId() == 0) {
            throw new IllegalArgumentException("被转发对象id不能为空！");
        }
        if (vo.getTargetUserId() == null) {
            throw new IllegalArgumentException("被转发用户id不能为空！");
        }

        UserDiaryForwardDO record = vo.record();
        record.setId(null);
        record.setCreateTime(new Date());

        record = forwardRepository.save(record);

        //清理缓存
        deleteCacheKey(record.getType(), record.getTargetId());

        //发消息
        Long fromUserId = vo.getFromUserId();
        Long toUserId = vo.getTargetUserId();

        //非转发本人的才发通知
        if (fromUserId.longValue() != toUserId.longValue()) {

            if (ForwardType.DIARY.getCode().intValue() == vo.getType()) {
                diaryMessageService.interactForwardDiary(fromUserId, toUserId);
            } else if (ForwardType.REPLY.getCode().intValue() == vo.getType()) {
                diaryMessageService.interactForwardComment(fromUserId, toUserId);
            }

        }

    }

    /**
     * 查询用户被转发的记录
     *
     * @param targetUserId
     * @param page
     * @param size
     * @return
     */
    public Page<ForwardMessageVO> msgPage(Long targetUserId, Integer page, Integer size) {
        if (targetUserId == null || targetUserId == 0) {
            throw new AccessDeniedException("未登录！");
        }

//        String msgUserListKey = RedisKeyConst.getUserDiaryForwardMessageList(targetUserId, page, size);

        Specification<UserDiaryForwardDO> specification = new Specification<UserDiaryForwardDO>() {
            @Override
            public Predicate toPredicate(Root<UserDiaryForwardDO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicateList = Lists.newArrayList();
                predicateList.add(criteriaBuilder.equal(root.get("targetUserId").as(Long.class), targetUserId));
                //并且不算转发人自己转发的记录
                predicateList.add(criteriaBuilder.notEqual(root.get("fromUserId").as(Long.class), targetUserId));
                return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
            }
        };
        Sort sort = new Sort(Sort.Direction.DESC, Lists.newArrayList("createTime"));
        PageRequest pageRequest = new PageRequest(page, size, sort).previous();
        org.springframework.data.domain.Page pageResult = forwardRepository.findAll(specification, pageRequest);
        List<UserDiaryForwardDO> content = pageResult.getContent();

        //小优化，将内层循环查询放到外层查一次
        Set<Long> fromUserIds = content.stream().map(UserDiaryForwardDO::getFromUserId).collect(Collectors.toSet());
        Map<Long, UserInfoDTO> userMap = this.mapByIds(fromUserIds);

        //2.3版本新加----需要提供转发后的日记id
        Set<Long> targetIds = content.stream().filter(userDiaryForward -> userDiaryForward.getStation() == 3).map(tzUserDiaryForward -> tzUserDiaryForward.getTargetId()).collect(Collectors.toSet());
        Map<String, Long> map = new HashMap<>();
        if (Objects.nonNull(targetIds)) {
            List<DiaryDO> diaryDOList = diaryService.findByforwardIds(new ArrayList<>(targetIds));
            map = diaryDOList.stream().collect(Collectors.toMap(o -> o.getCreateTime() + "-" + o.getForwardId() + "-" + o.getUserId(), o -> o.getId()));
        }

        List<ForwardMessageVO> vos = new ArrayList();
        //类型转换
        for (UserDiaryForwardDO info : content) {

            UserInfoDTO fromUser = userMap.get(info.getFromUserId());

            ForwardMessageVO vo = convertMessageVO(info, fromUser);
            vo.setResultId(map.get(info.getCreateTime() + "-" + info.getTargetId() + "-" + info.getFromUserId()));

            vos.add(vo);
        }


        Page<ForwardMessageVO> result = new Page<>();
        result.setRows(vos);
        result.setTotal(pageResult.getTotalElements());

        //TODO 缓存

        return result;
    }

    private ForwardMessageVO convertMessageVO(UserDiaryForwardDO info, UserInfoDTO fromUser) {

        //判断fromUser是否存在
        if (fromUser == null) {
            fromUser = new UserInfoDTO();
            fromUser.setId(info.getFromUserId());
            log.error("未查询到用户！userId={}", info.getFromUserId());
        }

        ForwardType typeEnum = ForwardType.create(info.getType());

        //截取20字符串的content
        String content = this.getTargetContentByType(typeEnum, info.getTargetId());

        ForwardMessageVO vo = ForwardMessageVO.builder()
                .createTime(info.getCreateTime())
                .fromUserId(info.getFromUserId())
                .fromUserName(fromUser.getName() == null ? "未知用户" : fromUser.getName())
                .fromUserAvatarUrl(fromUser.getAvatarUrl())
                .targetId(info.getTargetId())
                .forwardMessage(info.getForwardMessage())
                .targetContent(content)
                .targetUserId(info.getTargetUserId())
                .type(info.getType())
                .station(info.getStation())
                .build();

        return vo;
    }

    private String getTargetContentByType(ForwardType type, Long targetId) {
        if (type == null) {
            return null;
        }

        String content = null;
        switch (type) {
            case DIARY: {
                DiaryDO object = diaryService.findById(targetId);
                if (object != null) {
                    content = object.getContent();
                }

            }
            ;
            break;
            case REPLY: {
                UserDiaryReplyDO object = replyService.getByReplyId(targetId);
                if (object != null) {
                    content = object.getReplyContent();
                }
            }
            ;
            break;
            default: {
            }
        }

        return subContent(content);
    }


    public List<UserDiaryForwardDO> getByUnionId(Integer type, Long targetId) {
        String userDiaryFoardListCacheKey = RedisKeyConst.getUserDiaryForwardList(type, targetId);
        Object o = redisTemplate.opsForValue().get(userDiaryFoardListCacheKey);
        if (o == null) {
            List<UserDiaryForwardDO> userDiaryForwards = forwardRepository.findByTypeAndTargetId(type, targetId);
            if (userDiaryForwards != null) {
                redisTemplate.opsForValue().set(userDiaryFoardListCacheKey, userDiaryForwards, 300, TimeUnit.SECONDS);
            }
            redisTemplate.opsForValue().set(RedisKeyConst.getUserDiaryForwardCount(type, targetId), userDiaryForwards.size(), 300, TimeUnit.SECONDS);
            return userDiaryForwards;
        }
        return (List<UserDiaryForwardDO>) o;
    }

    private void deleteCacheKey(Integer type, Long targetId) {
        String userDiaryForwardListCacheKey = RedisKeyConst.getUserDiaryForwardList(type, targetId);
        String userDiaryForwardCountKey = RedisKeyConst.getUserDiaryForwardCount(type, targetId);
        redisTemplate.delete(userDiaryForwardListCacheKey);
        redisTemplate.delete(userDiaryForwardCountKey);
    }

    /**
     * 查询日记的转发数量
     *
     * @param diaryId
     * @return
     */
    public Long countDiaryForward(Long diaryId) {
        return countByUnionId(DIARY.getCode(), diaryId);
    }

    /**
     * 查询评论/回复的转发数量
     *
     * @param replyId
     * @return
     */
    public Long countReplyForward(Long replyId) {
        return countByUnionId(REPLY.getCode(), replyId);
    }

    private Long countByUnionId(Integer type, Long targetId) {
        if (type == null || targetId == null || targetId == 0) {
            throw new IllegalArgumentException("type/targetId必填参数为空！");
        }
        String userDiaryForwardCountKey = RedisKeyConst.getUserDiaryForwardCount(type, targetId);
        Object o = redisTemplate.opsForValue().get(userDiaryForwardCountKey);
        if (o != null) {
            return Long.parseLong(o.toString());
        }
        Long count = forwardRepository.countByTypeAndTargetId(type, targetId);
        redisTemplate.opsForValue().set(userDiaryForwardCountKey, count, 300, TimeUnit.SECONDS);
        return count;
    }

    /**
     * 截取定长字符串使用
     *
     * @param content
     * @return
     */
    private String subContent(String content) {
        return StringUtils.isBlank(content) ? null : content.length() > DISPLAY_LENGTH ? content.substring(0, DISPLAY_LENGTH) : content;
    }

}
